package com.fyy.rtspdemo.factories;

import com.alibaba.fastjson2.util.IOUtils;
import org.bytedeco.ffmpeg.avcodec.AVCodecParser;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;

import javax.servlet.AsyncContext;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ConverterFactories extends Thread implements Converter {

    public volatile boolean runing = true;

    private FFmpegFrameGrabber grabber;

    private FFmpegFrameRecorder recorder;

    private byte[] headers;

    private ByteArrayOutputStream stream;

    private String url;

    private List<AsyncContext> outEntitys;

    private String key;

    private Map<String,Converter> factories;

    public ConverterFactories(String url,String key,Map<String,Converter> factories,List<AsyncContext> outEntitys){
        this.url = url;
        this.key = key;
        this.outEntitys = outEntitys;
        this.factories = factories;
    }

    @Override
    public void run() {
        boolean isCloseGrabberAndResponse = true;
        try {
            grabber = new FFmpegFrameGrabber(url);
            grabber.start();
            int framerate = (int) grabber.getVideoFrameRate();
            if(avcodec.AV_CODEC_ID_H264 == grabber.getVideoCodec()
                    && (grabber.getAudioChannels() == 0 || avcodec.AV_CODEC_ID_AAC == grabber.getAudioCodec())){
                System.out.println("this url:{} converterFactories start " + url);
                // 此类型无需转码，降低资源消耗
                stream = new ByteArrayOutputStream();
                recorder = new FFmpegFrameRecorder(stream,grabber.getImageWidth(),grabber.getImageHeight()
                        ,grabber.getAudioChannels());
                recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                recorder.setFormat("flv");
                recorder.setVideoBitrate(grabber.getVideoBitrate());
                recorder.setFrameRate(framerate);
                recorder.setGopSize(framerate);
                recorder.setAudioChannels(grabber.getAudioChannels());
                recorder.start();

                Frame frame;
                while (null != (frame = grabber.grab())) {
                    recorder.record(frame);
                    byte[] bytes = stream.toByteArray();
                    writeResponse(bytes);
                }
            }else{
                isCloseGrabberAndResponse = false;
                ConverterTranFactories c = new ConverterTranFactories(url, key, outEntitys, factories, grabber);
                factories.put(key,c);
                c.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            closeConverter(isCloseGrabberAndResponse);
            completeResponse(isCloseGrabberAndResponse);
            System.out.println("this url converterFactories exit" + url);
        }
    }

    public void writeResponse(byte[] b){
        Iterator<AsyncContext> it = outEntitys.iterator();
        while (it.hasNext()){
            AsyncContext o = it.next();
            try {
                o.getResponse().getOutputStream().write(b);
            } catch (Exception e){
                System.out.println("移除一个输出");
                it.remove();
            }
        }
    }

    public void closeConverter(boolean isCloseGrabberAndResponse){
        if(isCloseGrabberAndResponse){
            IOUtils.close(grabber);
            factories.remove(this.key);
        }
        IOUtils.close(recorder);
        IOUtils.close(stream);
    }

    public void completeResponse(boolean isCloseGrabberAndResponse){
        if(isCloseGrabberAndResponse){
            Iterator<AsyncContext> it = outEntitys.iterator();
            while (it.hasNext()){
                AsyncContext o = it.next();
                o.complete();
            }
        }
    }

    @Override
    public String getKey() {
        return this.key;
    }

    @Override
    public String getUrl() {
        return this.url;
    }

    @Override
    public void addOutputStreamEntity(String key, AsyncContext entity) throws IOException {
        if(headers == null){
            outEntitys.add(entity);
        }else{
            entity.getResponse().getOutputStream().write(headers);
            entity.getResponse().getOutputStream().flush();
            outEntitys.add(entity);
        }
    }

    @Override
    public void exit() {
        this.runing = false;
        try {
            this.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
